06. 合并冲突
有时候合并会失败
大部分情况下,git 将能够成功地合并分支。但是,有时候 git 无法完全自动地进行合并。合并失败时,就称为 合并冲突 。
如果出现合并冲突,git 将尝试尽可能合并多的内容,然后将留下特殊选项(例如
>>>
和
<<<
),告诉你(没错,告诉作为程序员的你!)需要从何处手动修复。
什么导致了合并冲突
正如你所知道的,git 会跟踪文件中的代码行。如果完全相同的行在不同的文件中更改了,将产生合并冲突。例如,如果你在
alternate-sidebar-style
分支上并将侧栏的标题改为"Information About Me",git 应该选择哪个标题?你在两个分支上都更改了标题,因此 git 根本不知道你要保留哪个标题。它肯定不会随机选择一个标题!
我们来人为制造一个合并冲突,学习如何解决这种冲突。当你学会了后,就非常简单!当 git 不确定你要使用即将合并的分支中的哪些行时,就会出现合并冲突。因此我们需要在两个不同的分支上修改同一行,然后重设合并它们。
人为制造合并冲突!
当同一行在两个分支中都更改了时,就会出现合并冲突。我们在两个不同的分支上更改同一页面的标题:
-
更改
master分支上的标题 -
在最近修改
master分支的 commit 前面创建一个heading-update分支 - 更改同一标题
-
切换到
master分支 -
合并
heading-update分支
在分支上更改标题 1
因为
master
分支就像所有其他分支一样,也是个普通分支,我们在
master
分支上更改标题。将
<h1>
标题改为其他内容。对我来说,标题当前在第 13 行是"Adventure",我将其改为"Quest"。
做出更改后,保存文件并 commit 到仓库。
在分支上更改标题 2
现在我们需要创建不同的分支并在该分支上更新标题。
现在要注意的是,我们需要创建一个不是从 master 分支上分叉的分支。如果我们在从 master 分支上分叉的分支上做出更改,那么该更改将在此更改前面,git 将直接使用该更改,而不是使用我们刚刚在
master
上做出的更改。因此我们需要将该分支“放在过去”。
我们创建一个位于最近 commit 之前的 commit 上的分支。使用 git log 获取上一个 commit 的 SHA,并在该 commit 上创建一个分支。在创建
heading-update
分支后,我的 git log 输出结果如下所示:
终端显示了
git log --oneline --decorate --graph --all
的输出结果。当前被检出的分支为
heading-update
。
现在已经创建好分支,我们只需再次更新标题。确保更改在
master
分支上做出更改的同一行。我将第 13 行的"Adventure"改为"Crusade"。
然后保存文件,并将文件 commit 到仓库。
终端显示了
git log --oneline --decorate --graph --all
的输出结果。
master
分支和
heading-update
分支都更改了同一文件的同一行。
准备合并冲突
Task Description:
我们即将导致合并冲突,确保你的进度和我的一样:
Task Feedback:
很棒!我们继续。
确保你位于 master 分支(我们也可以在另一分支上操作,但是我习惯将
master
分支作为主分支,让另一个分支合并到该分支上)上,并合并
heading-update
分支:
$ git merge heading-update
你应该看到以下结果:
终端显示了导致合并冲突的合并输出结果。出现单词"CONFLICT",并且指出 index.html 文件中出现合并冲突。
合并冲突输出结果解释
终端中显示的输出结果为:
$ git merge heading-update
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.
注意在
git merge heading-update
命令之后,git 尝试合并在两个分支上都更改了的文件 (
index.html
),但是出现冲突。此外,它告诉你发生了什么:"Automatic merge failed; fix conflicts and then commit the result"。
还记得
git status
命令吗?在处理合并冲突时,该命令将非常有用。
合并冲突的状态?
SOLUTION:
- You have unmerged paths
- Unmerged paths
- fix conflicts and run "git commit"
-
use "git add
..." to mark resolution
git status
的输出结果告诉我们
index.html
中存在合并冲突。因此在代码编辑器中查看该文件!
代码编辑器显示了合并冲突指示符。
合并冲突指示符解释
编辑器具有以下合并冲突指示符:
-
<<<<<<< HEAD此行下方的所有内容(直到下个指示符)显示了当前分支上的行 -
||||||| merged common ancestors此行下方的所有内容(直到下个指示符)显示了原始行的内容 -
=======表示原始行内容的结束位置,之后的所有行(直到下个指示符)是被合并的当前分支上的行的内容 -
>>>>>>> heading-update是要被合并的分支(此例中是heading-update分支)上的行结束指示符
解决合并冲突
git 使用合并冲突指示符来告诉你两个不同分支上的哪些行导致了合并冲突,以及原始行是什么。要解决合并冲突,你需要:
- 选择保留哪些行
- 删掉所有带指示符的行
因为某种原因,我不太喜欢现在的"Crusade"一词,但是"Quest"也不太合适。要不将标题设为"Adventurous Quest"?
代码编辑器显示合并冲突指示符已被删除,并且标题已更新。
commit 合并冲突
删掉所有包含合并冲突指示符的行并选择保留哪个标题后,直接保存文件,并将其添加到暂存区,然后 commit!就像普通合并一样,代码编辑器会弹出,并让你提供 commit 消息。和之前一样,我们经常会使用自动生成的合并 commit 消息,因此在编辑器打开后,直接关闭编辑器并使用自动生成的 commit 消息。
就这些内容!当你明白合并指示符所显示的内容后,合并冲突就没那么复杂了。
合并冲突指示符可以被 commit 吗?